package models

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"image-sever-grpc/image-web-server/config"
	"image-sever-grpc/image-web-server/dao/model"
	oss "image-sever-grpc/oss-server/proto"
	"path"
	"path/filepath"
	"strings"
	"time"
)

// BatchCreateImageWithFiles 批量创建图片记录（直接返回成功的图片对象）
func BatchCreateImageWithFiles(
	images []*model.Image,
	originalContents [][]byte,
	thumbContents [][]byte,
) ([]*model.Image, []string, error) {

	// 基本参数校验
	if len(images) != len(originalContents) || len(images) != len(thumbContents) {
		return nil, nil, errors.New("参数长度不匹配")
	}
	if len(images) == 0 {
		return []*model.Image{}, []string{}, nil
	}

	var (
		successImages []*model.Image // 直接收集成功的图片对象（包含ID）
		failedNames   []string
		batchFiles    []*oss.UploadToOSSRequest
	)

	// 1. 准备上传文件列表
	for i, img := range images {
		// 生成缩略图路径
		if len(thumbContents[i]) > 0 && img.ThumbOssKey == "" {
			ext := filepath.Ext(img.OssKey)
			img.ThumbOssKey = strings.TrimSuffix(img.OssKey, ext) + "_thumb" + ext
			img.ThumbFileSize = int64(len(thumbContents[i]))
		}

		// 添加原图和缩略图到批量上传列表
		batchFiles = append(batchFiles, &oss.UploadToOSSRequest{
			ObjectKey: img.OssKey,
			Content:   originalContents[i],
		})
		if len(thumbContents[i]) > 0 {
			batchFiles = append(batchFiles, &oss.UploadToOSSRequest{
				ObjectKey: img.ThumbOssKey,
				Content:   thumbContents[i],
			})
		}
	}

	// 2. 批量上传到OSS
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	batchResp, err := config.OSSClient.UploadBatchToOSS(ctx, &oss.UploadBatchToOSSRequest{
		Files: batchFiles,
	})
	if err != nil {
		return nil, nil, fmt.Errorf("批量上传失败: %w", err)
	}

	// 3. 检查上传结果
	uploadSuccess := make([]bool, len(images))
	fileIndex := 0
	for i := range images {
		img := images[i]
		// 检查原图上传结果
		if fileIndex >= len(batchResp.Results) || !batchResp.Results[fileIndex].Error.Success {
			failedNames = append(failedNames, img.FileName)
			fileIndex++
			if len(thumbContents[i]) > 0 {
				fileIndex++
			}
			continue
		}

		// 检查缩略图
		fileIndex++
		if len(thumbContents[i]) > 0 {
			if fileIndex >= len(batchResp.Results) || !batchResp.Results[fileIndex].Error.Success {
				// 回滚原图
				config.OSSClient.DeleteFromOSS(context.Background(), &oss.DeleteFromOSSRequest{
					ObjectKey: img.OssKey,
				})
				failedNames = append(failedNames, img.FileName)
				fileIndex++
				continue
			}
			fileIndex++
		}

		uploadSuccess[i] = true // 标记为上传成功
	}

	// 4. 保存到数据库
	tx, err := config.DB.Begin()
	if err != nil {
		return nil, failedNames, fmt.Errorf("开启事务失败: %w", err)
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare(`
		INSERT INTO images 
		 (album_id, user_id, title, description, tags,
		  oss_key, file_name, file_size, 
		  thumb_oss_key, thumb_file_size, created_at) 
		 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
	if err != nil {
		return nil, failedNames, fmt.Errorf("准备SQL失败: %w", err)
	}
	defer stmt.Close()

	now := time.Now()
	var dbFailedKeys []string

	for i, img := range images {
		if !uploadSuccess[i] {
			continue // 跳过上传失败的
		}

		// 执行插入
		img.CreatedAt = now
		result, err := stmt.Exec(
			img.AlbumID, img.UserID, img.Title, img.Description, img.Tags,
			img.OssKey, img.FileName, img.FileSize,
			img.ThumbOssKey, img.ThumbFileSize, now,
		)
		if err != nil {
			dbFailedKeys = append(dbFailedKeys, img.OssKey, img.ThumbOssKey)
			failedNames = append(failedNames, img.FileName)
			continue
		}

		// 关键：获取插入后的ID并赋值给img，然后添加到成功列表
		id, _ := result.LastInsertId()
		img.ID = id                                // 为图片对象赋值ID
		successImages = append(successImages, img) // 直接收集成功的图片对象
	}

	// 提交事务
	if err := tx.Commit(); err != nil {
		return nil, failedNames, fmt.Errorf("提交事务失败: %w", err)
	}

	// 回滚数据库失败的文件
	if len(dbFailedKeys) > 0 {
		config.OSSClient.DeleteObjects(context.Background(), &oss.DeleteObjectsRequest{
			Keys: dbFailedKeys,
		})
	}

	// 返回成功的图片对象列表（包含ID）和失败列表
	return successImages, failedNames, nil
}

// CreateImageWithFiles 完整创建图片记录（包含文件上传和数据库存储）
func CreateImageWithFiles(
	image *model.Image,
	originalContent []byte,
	thumbContent []byte,
) (int64, error) {
	// 1. 生成缩略图存储路径
	if len(thumbContent) > 0 && image.ThumbOssKey == "" {
		ext := filepath.Ext(image.OssKey)
		image.ThumbOssKey = strings.TrimSuffix(image.OssKey, ext) + "_thumb" + ext
		image.ThumbFileSize = int64(len(thumbContent))
	}

	// 2. 上传原图到OSS（调用oss-server）
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	uploadResp, err := config.OSSClient.UploadToOSS(ctx, &oss.UploadToOSSRequest{
		ObjectKey: image.OssKey,
		Content:   originalContent,
	})
	if err != nil || !uploadResp.Error.Success {
		errMsg := "上传原图失败"
		if uploadResp != nil && uploadResp.Error.ErrorMsg != "" {
			errMsg = fmt.Sprintf("%s: %s", errMsg, uploadResp.Error.ErrorMsg)
		} else if err != nil {
			errMsg = fmt.Sprintf("%s: %v", errMsg, err)
		}
		return 0, errors.New(errMsg)
	}

	// 3. 上传缩略图到OSS（失败则回滚原图）
	if len(thumbContent) > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		thumbResp, err := config.OSSClient.UploadToOSS(ctx, &oss.UploadToOSSRequest{
			ObjectKey: image.ThumbOssKey,
			Content:   thumbContent,
		})
		if err != nil || !thumbResp.Error.Success {
			// 回滚操作
			rollbackCtx, rollbackCancel := context.WithTimeout(context.Background(), 5*time.Second)
			defer rollbackCancel()
			config.OSSClient.DeleteFromOSS(rollbackCtx, &oss.DeleteFromOSSRequest{
				ObjectKey: image.OssKey,
			})

			errMsg := "上传缩略图失败"
			if thumbResp != nil && thumbResp.Error.ErrorMsg != "" {
				errMsg = fmt.Sprintf("%s: %s", errMsg, thumbResp.Error.ErrorMsg)
			} else if err != nil {
				errMsg = fmt.Sprintf("%s: %v", errMsg, err)
			}
			return 0, errors.New(errMsg)
		}
	}

	// 4. 保存记录到数据库（失败则回滚所有文件）
	image.CreatedAt = time.Now()
	result, err := config.DB.Exec(
		`INSERT INTO images 
		 (album_id, user_id, title, description, tags,
		  oss_key, file_name, file_size, 
		  thumb_oss_key, thumb_file_size, created_at) 
		 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		image.AlbumID, image.UserID, image.Title,
		image.Description, image.Tags,
		image.OssKey, image.FileName, image.FileSize,
		image.ThumbOssKey, image.ThumbFileSize,
		image.CreatedAt,
	)
	if err != nil {
		// 回滚已上传的文件
		rollbackCtx, rollbackCancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer rollbackCancel()
		config.OSSClient.DeleteFromOSS(rollbackCtx, &oss.DeleteFromOSSRequest{
			ObjectKey: image.OssKey,
		})
		if image.ThumbOssKey != "" {
			config.OSSClient.DeleteFromOSS(rollbackCtx, &oss.DeleteFromOSSRequest{
				ObjectKey: image.ThumbOssKey,
			})
		}
		return 0, fmt.Errorf("保存图片记录失败: %w", err)
	}

	return result.LastInsertId()
}

// GetImageByID 根据ID查询图片信息
func GetImageByID(id int64) (*model.Image, error) {
	var img model.Image
	err := config.DB.QueryRow(
		`SELECT id, album_id, user_id, title, oss_key, file_name, file_size, thumb_oss_key, thumb_file_size, created_at 
		 FROM images WHERE id = ?`,
		id,
	).Scan(
		&img.ID, &img.AlbumID, &img.UserID, &img.Title,
		&img.OssKey, &img.FileName, &img.FileSize, &img.ThumbOssKey, &img.ThumbFileSize, &img.CreatedAt,
	)
	if err == sql.ErrNoRows {
		return nil, errors.New("图片不存在")
	}
	return &img, err
}

func UpdateImage(image *model.Image) error {
	// 1. 查询旧图片信息
	oldImage, err := GetImageByID(image.ID)
	if err != nil {
		return err
	}
	// 2. 如果标题变更，处理OSS文件名更新
	var newOSSKey, newThumbOSSKey string
	if image.Title != oldImage.Title {
		// 调用oss-server清理文件名
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		sanitizeResp, err := config.OSSClient.SanitizeFileName(ctx, &oss.SanitizeFileNameRequest{
			Name: image.Title,
		})
		if err != nil || !sanitizeResp.Error.Success {
			errMsg := "清理文件名失败"
			if sanitizeResp != nil && sanitizeResp.Error.ErrorMsg != "" {
				errMsg = fmt.Sprintf("%s: %s", errMsg, sanitizeResp.Error.ErrorMsg)
			} else if err != nil {
				errMsg = fmt.Sprintf("%s: %v", errMsg, err)
			}
			return errors.New(errMsg)
		}

		// 生成新的文件名
		newFileName := sanitizeResp.SanitizedName + path.Ext(oldImage.FileName)

		// 生成新的OSS路径
		oldOSSKeyDir := path.Dir(oldImage.OssKey)
		newOSSKey = path.Join(oldOSSKeyDir, newFileName)

		// 生成新的缩略图OSS路径
		ext := filepath.Ext(newOSSKey)
		newThumbOSSKey = strings.TrimSuffix(newOSSKey, ext) + "_thumb" + ext

		// 重命名OSS文件（原图）
		ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		renameResp, err := config.OSSClient.RenameOSSObject(ctx, &oss.RenameOSSObjectRequest{
			OldKey: oldImage.OssKey,
			NewKey: newOSSKey,
		})
		if err != nil || !renameResp.Error.Success {
			errMsg := "重命名OSS文件失败"
			if renameResp != nil && renameResp.Error.ErrorMsg != "" {
				errMsg = fmt.Sprintf("%s: %s", errMsg, renameResp.Error.ErrorMsg)
			} else if err != nil {
				errMsg = fmt.Sprintf("%s: %v", errMsg, err)
			}
			return errors.New(errMsg)
		}

		// 重命名缩略图文件
		if oldImage.ThumbOssKey != "" {
			ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
			defer cancel()
			thumbRenameResp, err := config.OSSClient.RenameOSSObject(ctx, &oss.RenameOSSObjectRequest{
				OldKey: oldImage.ThumbOssKey,
				NewKey: newThumbOSSKey,
			})
			if err != nil || !thumbRenameResp.Error.Success {
				// 回滚原图重命名
				rollbackCtx, rollbackCancel := context.WithTimeout(context.Background(), 5*time.Second)
				defer rollbackCancel()
				config.OSSClient.RenameOSSObject(rollbackCtx, &oss.RenameOSSObjectRequest{
					OldKey: newOSSKey,
					NewKey: oldImage.OssKey,
				})

				errMsg := "重命名缩略图文件失败"
				if thumbRenameResp != nil && thumbRenameResp.Error.ErrorMsg != "" {
					errMsg = fmt.Sprintf("%s: %s", errMsg, thumbRenameResp.Error.ErrorMsg)
				} else if err != nil {
					errMsg = fmt.Sprintf("%s: %v", errMsg, err)
				}
				return errors.New(errMsg)
			}
		}
	}
	// 3. 执行数据库更新
	_, err = config.DB.Exec(
		`UPDATE images 
		 SET title = ?, description = ?, tags = ?, 
		     oss_key = COALESCE(?, oss_key), 
		     thumb_oss_key = COALESCE(?, thumb_oss_key)
		 WHERE id = ? AND user_id = ?`,
		image.Title,
		image.Description,
		image.Tags,
		newOSSKey,      // 新的原图路径（若无变更则为NULL）
		newThumbOSSKey, // 新的缩略图路径（若无变更则为NULL）
		image.ID,
		image.UserID, // 验证所有者身份
	)
	if err != nil {
		return fmt.Errorf("数据库更新失败: %w", err)
	}
	return nil
}

// DeleteImageByID 删除图片（含OSS文件和数据库记录）
func DeleteImageByID(id, userID int64) error {
	// 1. 查询图片完整信息
	img, err := GetImageByID(id)
	if err != nil {
		return err
	}

	// 2. 验证权限
	if img.UserID != userID {
		return errors.New("无删除权限")
	}

	// 3. 删除OSS中的原图
	if img.OssKey != "" {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		deleteResp, err := config.OSSClient.DeleteFromOSS(ctx, &oss.DeleteFromOSSRequest{
			ObjectKey: img.OssKey,
		})
		if err != nil || !deleteResp.Error.Success {
			errMsg := "删除原图失败"
			if deleteResp != nil && deleteResp.Error.ErrorMsg != "" {
				errMsg = fmt.Sprintf("%s: %s", errMsg, deleteResp.Error.ErrorMsg)
			} else if err != nil {
				errMsg = fmt.Sprintf("%s: %v", errMsg, err)
			}
			return errors.New(errMsg)
		}
	}

	// 4. 删除OSS中的缩略图
	if img.ThumbOssKey != "" {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		thumbDeleteResp, err := config.OSSClient.DeleteFromOSS(ctx, &oss.DeleteFromOSSRequest{
			ObjectKey: img.ThumbOssKey,
		})
		if err != nil || !thumbDeleteResp.Error.Success {
			errMsg := "删除缩略图失败"
			if thumbDeleteResp != nil && thumbDeleteResp.Error.ErrorMsg != "" {
				errMsg = fmt.Sprintf("%s: %s", errMsg, thumbDeleteResp.Error.ErrorMsg)
			} else if err != nil {
				errMsg = fmt.Sprintf("%s: %v", errMsg, err)
			}
			return errors.New(errMsg)
		}
	}

	// 5. 删除数据库记录
	_, err = config.DB.Exec(
		"DELETE FROM images WHERE id = ? AND user_id = ?",
		id, userID,
	)
	return err
}

// GetImagesByAlbumID 按相册ID查询图片（分页）
func GetImagesByAlbumID(albumID int64, page, limit int) ([]model.Image, int64, error) {
	offset := (page - 1) * limit

	// 1. 查询图片列表
	rows, err := config.DB.Query(
		`SELECT 
			id, album_id, user_id, title, description, tags,
			oss_key, file_name, file_size, 
			thumb_oss_key, thumb_file_size, created_at 
		 FROM images 
		 WHERE album_id = ? 
		 ORDER BY created_at DESC 
		 LIMIT ? OFFSET ?`,
		albumID, limit, offset,
	)
	if err != nil {
		return nil, 0, fmt.Errorf("查询图片列表失败: %w", err)
	}
	defer rows.Close()

	// 2. 解析查询结果
	var images []model.Image
	for rows.Next() {
		var img model.Image
		if err := rows.Scan(
			&img.ID, &img.AlbumID, &img.UserID, &img.Title,
			&img.Description, &img.Tags,
			&img.OssKey, &img.FileName, &img.FileSize,
			&img.ThumbOssKey, &img.ThumbFileSize,
			&img.CreatedAt,
		); err != nil {
			return nil, 0, fmt.Errorf("解析图片信息失败: %w", err)
		}
		images = append(images, img)
	}

	// 检查行迭代错误
	if err := rows.Err(); err != nil {
		return nil, 0, fmt.Errorf("遍历图片记录失败: %w", err)
	}

	// 3. 查询总条数
	var total int64
	err = config.DB.QueryRow(
		"SELECT COUNT(*) FROM images WHERE album_id = ?",
		albumID,
	).Scan(&total)
	if err != nil {
		return nil, 0, fmt.Errorf("查询图片总数失败: %w", err)
	}

	return images, total, nil
}

// DeleteImagesByAlbumID 按相册ID删除所有图片
func DeleteImagesByAlbumID(albumID int64) error {
	// 1. 查询所有图片的OSS路径
	rows, err := config.DB.Query(
		"SELECT oss_key, thumb_oss_key FROM images WHERE album_id = ?",
		albumID,
	)
	if err != nil {
		return fmt.Errorf("查询图片信息失败: %w", err)
	}
	defer rows.Close()

	// 收集需要删除的OSS路径
	var allOssKeys []string
	for rows.Next() {
		var ossKey, thumbOssKey string
		if err := rows.Scan(&ossKey, &thumbOssKey); err != nil {
			return fmt.Errorf("读取图片信息失败: %w", err)
		}
		if ossKey != "" {
			allOssKeys = append(allOssKeys, ossKey)
		}
		if thumbOssKey != "" {
			allOssKeys = append(allOssKeys, thumbOssKey)
		}
	}

	// 检查行迭代错误
	if err := rows.Err(); err != nil {
		return fmt.Errorf("遍历图片记录失败: %w", err)
	}

	// 2. 批量删除OSS中的原图和缩略图
	if len(allOssKeys) > 0 {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		deleteResp, err := config.OSSClient.DeleteObjects(ctx, &oss.DeleteObjectsRequest{
			Keys: allOssKeys,
		})
		if err != nil || !deleteResp.Error.Success {
			errMsg := "批量删除图片（含缩略图）失败"
			if deleteResp != nil && deleteResp.Error.ErrorMsg != "" {
				errMsg = fmt.Sprintf("%s: %s", errMsg, deleteResp.Error.ErrorMsg)
			} else if err != nil {
				errMsg = fmt.Sprintf("%s: %v", errMsg, err)
			}
			return errors.New(errMsg)
		}
	}

	// 3. 删除数据库中该相册的所有图片记录
	_, err = config.DB.Exec("DELETE FROM images WHERE album_id = ?", albumID)
	if err != nil {
		return fmt.Errorf("删除数据库图片记录失败: %w", err)
	}

	return nil
}
